#include <stdio.h>
#include <stdlib.h>
#include "raylib.h"

// 仙人掌种类数
#define CACTUS_TYPE_TOTAL 6
// 仙人掌总数
#define CACTUS_TOTAL 10
// 最大速度
#define MAX_SPEED 500
// 鸟总数
#define BIRDS_TOTAL 5
// 鸟动画间隔
#define BIRD_FRAME_INTERVAL 1 / 4.f

// 游戏状态
typedef enum GameState {
    START,      // 开始
    PLAYING,    // 游戏
    END         // 结束
} GameState;

// 恐龙状态
typedef enum DinoState {
    WALK,   // 走
    JUMP,   // 跳
    DUCK    // 蹲下
} DinoState;

// 恐龙动画信息结构体
typedef struct DinoAnimInfo {
    int frameTotal;         // 帧总数
    Rectangle rects[5];     // 矩形区域数组
    float frameInterval;    // 帧间隔
    Rectangle collRect;     // 碰撞区域（相对于恐龙的位置）
} DinoAnimInfo;

// 仙人掌纹理信息
typedef struct CactusTextureInfo {
    Rectangle rectTexture;  // 纹理区域
    float yOffset;          // y方向偏移
    Rectangle collRect;     // 碰撞区域
} CactusTextureInfo;

// 仙人掌信息
typedef struct CactusInfo {
    int cactusType; // 类型
    Vector2 pos;    // 位置
} CactusInfo;

// 恐龙
typedef struct Dino {
    DinoState state;    // 恐龙状态
    int frameIdx;       // 帧索引
    float frameTime;    // 帧时间
    Vector2 pos;        // 位置
    float ySpeed;       // y方向的速度
} Dino;

// 恐龙动画信息
const DinoAnimInfo dinoAnimInfo[] = {
    // 走路
    {2, {{936, 0, 44, 49}, {980, 0, 44, 49}}, 1.f / 6, {5, 10, 34, 29}, },
    // 跳跃
    { 1, {{848, 0, 44, 49}}, 1.f, {5, 10, 34, 29} },
    // 蹲下
    { 2, {{1112, 0, 59, 49}, {1171, 0, 59, 49}}, 1.f / 6, {5, 20, 40, 16}, }
};

// 仙人掌纹理信息
const CactusTextureInfo cactusTextureInfo[CACTUS_TYPE_TOTAL] = {
    { {228, 0, 17, 37}, 15, {1, 18, 15, 33}, },
    { {245, 0, 34, 37}, 15, {1, 18, 32, 33}, },
    { {279, 0, 51, 37}, 15, {1, 18, 49, 33}, },
    { {332, 0, 25, 52}, 0, {1, 1, 23, 50} },
    { {357, 0, 50, 52}, 0, {1, 1, 48, 50}, },
    { {407, 0, 75, 52}, 0, {1, 1, 73, 50} },
};

// 恐龙初始位置
const Vector2 DINO_INIT_POS = {15, 135};
// 跳跃y方向初始速度
const float JUMP_INIT_SPEED = -800;
// 重力加速度
const float GRAVITY = 2500;

Texture texture;
GameState gameState;
Dino dino;
float mapxoffset = 0;
float mapSpeed = 0;
float addObsDis = 0;
float score = 0;
CactusInfo* cactuses[CACTUS_TOTAL];

// 鸟的结构体
typedef struct Bird {
    int frameIndex;     // 帧索引
    float frameTime;    // 帧时间
    Vector2 pos;        // 位置
} Bird;

// 鸟的图片区域
Rectangle birdTextureRects[2] = { {134, 2, 46, 40}, {180, 2, 46, 40}, };

// 鸟的碰撞区域 相对于起点位置
Rectangle birdCollRect = { 2, 13, 42, 15 };

Bird* birds[BIRDS_TOTAL];

void init();
void loadTextures();
void initGame();
void drawBg();
void driveHorizon(float dt);
void drawHorizon();
void driveDino(float dt);
void drawDino();
void setDinoState(DinoState state);
void driveAddObstacles(float dt);
void driveCactuses(float dt);
void drawCactuses();
void driveBirds(float dt);
void drawBirds();
void driveAddScore(float dt);
void drawScore();
void driveSpeedUp(float dt);
void drawTip();

void checkKeyDown();

int main() {
    init();
    while (!WindowShouldClose()) {
        checkKeyDown();
        float dt = GetFrameTime();
        BeginDrawing();
            drawBg();
            driveHorizon(dt);
            drawHorizon();
            driveDino(dt);
            drawDino();
            driveCactuses(dt);
            drawCactuses();
            driveAddObstacles(dt);
            driveAddScore(dt);
            driveBirds(dt);
            drawBirds();
            drawScore();
            driveSpeedUp(dt);
            drawTip();
        EndDrawing();
    }

    CloseWindow();
    return 0;
}

void init() {
    InitWindow(600, 300, "Google Dino");
    SetTargetFPS(60);
    loadTextures();
    initGame();
}

void initGame() {
    gameState = START;
    mapxoffset = 0;
    mapSpeed = 350;
    score = 0;

    setDinoState(WALK);
    dino.pos = DINO_INIT_POS;
    for (int i = 0; i != CACTUS_TOTAL; i++) {
        if (cactuses[i]) free(cactuses[i]);
        cactuses[i] = NULL;
    }
    for (int i = 0; i != BIRDS_TOTAL; i++) {
        if (birds[i]) free(birds[i]);
        birds[i] = NULL;
    }

    addObsDis = GetRandomValue(250, 400);
}

void loadTextures() {
    texture = LoadTexture("res/texture.png");
}

void drawBg() {
    ClearBackground((Color){231, 231, 231, 255});
}

void driveHorizon(float dt) {
    if (gameState == END) return;
    mapxoffset -= mapSpeed * dt;
    if (mapxoffset < -2231 / 2) mapxoffset = 0;
}

void drawHorizon() {
    DrawTextureRec(
        texture,
        (Rectangle){2, 54, 2231, 16},
        (Vector2){mapxoffset, 170},
        WHITE
    );
}

void driveDino(float dt) {
    if (gameState == END) return;
    DinoAnimInfo animInfo = dinoAnimInfo[dino.state];
    dino.frameTime += dt;
    if (dino.frameTime > animInfo.frameInterval) {
        dino.frameTime -= animInfo.frameInterval;
        dino.frameIdx = ++dino.frameIdx % animInfo.frameTotal;
    }
    if (dino.state == JUMP) {
        dino.pos.y += dino.ySpeed * dt;
        dino.ySpeed += GRAVITY * dt;
        if (dino.pos.y > DINO_INIT_POS.y) {
            dino.pos.y = DINO_INIT_POS.y;
            setDinoState(WALK);
        }
    }
}

void drawDino() {
    DrawTextureRec(
        texture,
        dinoAnimInfo[dino.state].rects[dino.frameIdx],
        dino.pos,
        WHITE
    );
}

void setDinoState(DinoState state) {
    dino.state = state;
    dino.frameIdx = 0;
    dino.frameTime = 0;
}

void checkKeyDown() {
    if (gameState == START) {
        if (GetKeyPressed()) gameState = PLAYING;
    } else if (gameState == END) {
        if (IsKeyPressed(KEY_SPACE)) {
            initGame();
            gameState = PLAYING;
        }
        return;
    }

    if (IsKeyPressed(KEY_UP) || IsKeyPressed(KEY_SPACE)) {
        if (dino.state != JUMP) {
            setDinoState(JUMP);
            dino.ySpeed = JUMP_INIT_SPEED;
        }
    } else if (IsKeyDown(KEY_DOWN)) {
        if (dino.state != DUCK && dino.state != JUMP) setDinoState(DUCK);
    } else {
        if (dino.state == DUCK) setDinoState(WALK);
    }
}

void driveAddObstacles(float dt) {
    if (gameState != PLAYING) return;
    addObsDis -= mapSpeed * dt;
    if (addObsDis < 0) {
        addObsDis = GetRandomValue(250, 400);

        if (score > 100 && GetRandomValue(0, 3) == 0) {
            Bird* bird = malloc(sizeof(Bird)); 
            bird->frameIndex = 0;
            bird->frameTime = 0;
            bird->pos = (Vector2){650, 130 - GetRandomValue(0, 50)};
            for (int i = 0; i != 5; i++) {
                if (birds[i] == NULL) {
                    birds[i] = bird;
                    break;
                }
            }
        } else {
            int cactusType = GetRandomValue(0, CACTUS_TYPE_TOTAL);
            CactusInfo* cactus = malloc(sizeof(CactusInfo));
            cactus->cactusType = cactusType;
            cactus->pos = (Vector2){650, 130};
            for (int i = 0; i != CACTUS_TOTAL; i++) {
                if (cactuses[i] == NULL) {
                    cactuses[i] = cactus;
                    break;
                }
            }
        }
    }
}

void driveCactuses(float dt) {
    if (gameState == END) return;
    Rectangle recDino = {
        dino.pos.x + dinoAnimInfo[dino.state].collRect.x,
        dino.pos.y + dinoAnimInfo[dino.state].collRect.y,
        dinoAnimInfo[dino.state].collRect.width,
        dinoAnimInfo[dino.state].collRect.height
    };
    for (int i = 0; i != CACTUS_TOTAL; i++) {
        CactusInfo* cactus = cactuses[i];
        if (cactus == NULL) continue;
        cactus->pos.x -= mapSpeed * dt;
        if (cactus->pos.x < -100) {
            free(cactus);
            cactuses[i] = NULL;
            continue;
        }
        // 检测与恐龙碰撞
        int cactusType = cactuses[i]->cactusType;
        CactusTextureInfo textureInfo = cactusTextureInfo[cactusType];
        Rectangle recCactus = {
            cactuses[i]->pos.x + textureInfo.collRect.x,
            cactuses[i]->pos.y + textureInfo.collRect.y,
            textureInfo.collRect.width,
            textureInfo.collRect.height,
        };
        if (CheckCollisionRecs(recDino, recCactus)) {
            gameState = END;
            return;
        }
    }
}

void drawCactuses() {
    for (int i = 0; i != CACTUS_TOTAL; i++) {
        if (cactuses[i] == NULL) continue;
        int cactusType = cactuses[i]->cactusType;
        CactusTextureInfo textureInfo = cactusTextureInfo[cactusType];
        DrawTextureRec(
            texture,
            textureInfo.rectTexture,
            (Vector2){
                cactuses[i]->pos.x,
                cactuses[i]->pos.y + textureInfo.yOffset,
            },
            WHITE
        );
    }
}

void driveBirds(float dt) {
    if (gameState != PLAYING) return;
    for (int i = 0; i != BIRDS_TOTAL; i++) {
        if (birds[i] == NULL) continue;
        birds[i]->frameTime += dt;
        if (birds[i]->frameTime > BIRD_FRAME_INTERVAL) {
            birds[i]->frameTime = 0;
            birds[i]->frameIndex = ++birds[i]->frameIndex % 2;
        }

        birds[i]->pos.x -= dt * mapSpeed;
        if (birds[i]->pos.x < -100) {
            free(birds[i]);
            birds[i] = NULL;
            continue;
        }
    }
}

void drawBirds() {
    for (int i = 0; i != BIRDS_TOTAL; i++) {
        if (birds[i] == NULL) continue;
        DrawTextureRec(texture,
            birdTextureRects[birds[i]->frameIndex],
            birds[i]->pos, WHITE
        );
    }
}

void driveAddScore(float dt) {
    if (gameState != PLAYING) return;
    score += mapSpeed * dt / 10;
}

void drawScore() {
    char buf[20];
    sprintf(buf, "%d", (int)(score));
    DrawText(buf, 500, 50, 25, BLACK);
}

void driveSpeedUp(float dt) {
    if (gameState != PLAYING) return;
    if (mapSpeed >= MAX_SPEED) return;
    mapSpeed += dt * 2;
}

void drawTip() {
    if (gameState == START) {
        DrawText("press space to start", 50, 200, 20, GRAY);
    } else if (gameState == END) {
        DrawText("press space to restart", 50, 200, 20, GRAY);
    }
}
